use std::collections::HashMap;

use crate::Builder;
use serde::{Deserialize, Serialize};
use serde_json::json;

use crate::prelude::*;
use crate::{Request, Response};

pub trait ResponseBuilderExt {
    fn string(self, s: impl Into<String>) -> Result<Response, Error>;
    fn html(self, s: impl Into<String>) -> Result<Response, Error>;
    fn json(self, data: impl Serialize) -> Result<Response, Error>;
}

impl ResponseBuilderExt for Builder {
    fn string(self, s: impl Into<String>) -> Result<Response, Error> {
        let res = s.into();
        let res = self.header("Content-Type", "text/plain").body(res.into())?;
        Ok(res)
    }

    fn json(self, data: impl Serialize) -> Result<Response, Error> {
        let res = serde_json::to_string(&data)?;
        let res = self
            .header("Content-Type", "application/json")
            .body(res.into())?;
        Ok(res)
    }

    fn html(self, s: impl Into<String>) -> Result<Response, Error> {
        let html = s.into();
        let res = self.header("Content-Type", "text/html").body(html.into())?;
        Ok(res)
    }
}

pub trait RequestExt {
    fn form(&self, key: impl Into<String>) -> Result<String>;
    fn query(&self, key: impl Into<String>) -> Result<String>;
    fn json<T: Deserialize>(&self) -> Result<T>;
}

impl RequestExt for Request {
    fn form(&self, key: impl Into<String>) -> Result<String> {
        let headers = self.headers();
        let query = self.uri().query().ok_or("query not found")?;

        Ok(query.into())
    }

    fn query(&self, key: impl Into<String>) -> Result<String> {
        let query = self.uri().query().ok_or("query not found")?;
        let query: HashMap<String, String> = serde_querystring::from_str(query)?;
        let val = query.get(&key.into()).unwrap();

        Ok(val.into())
    }

    fn json<T: Deserialize>(&self) -> Result<T> {
        let body = self.body();
        let bytes = hyper::body::to_bytes(body.clone());
        let val = serde_json::from_str(bytes.into())?;

        Ok(val)
    }
}
